﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
using ControlLibrary.Win32;
using IATHook;

namespace ControlLibrary.Utility
{
    public class PaintingContext : IDeviceContext
    {
        private Color backColor;
        private int alpha;
        private Graphics graphics;
        private ImageAttributes imageAttributes;
        private IntPtr hdc;
        private Bitmap contextBitmap;
        private Bitmap bufferBitmap;

        public ImageAttributes ImageAttributes
        {
            get { return imageAttributes ?? (imageAttributes = new ImageAttributes()); }
        }

        public IntPtr Hwnd { get; set; }

        public Bitmap Bitmap
        {
            get { return contextBitmap; }
            set
            {
                contextBitmap = value;
                bufferBitmap = new Bitmap(value.Width, value.Height);
            }
        }

        public Bitmap BufferBitmap
        {
            get { return bufferBitmap; }
        }

        public Rectangle ClientRectangle { get; set; }

        public Control Control { get; set; }

        public Color BackColor
        {
            get { return backColor; }
            set
            {
                if (backColor != value)
                {
                    imageAttributes = new ImageAttributes();
                    var colorMap = new ColorMap { OldColor = Color.FromArgb(255, backColor), NewColor = Color.FromArgb(alpha, backColor) };
                    var colorMaps = new ColorMap[] { colorMap };
                    imageAttributes.SetRemapTable(colorMaps);
                }
                backColor = value;
            }
        }

        public int Alpha
        {
            get { return alpha; }
            set
            {
                if (alpha != value)
                {
                    imageAttributes = new ImageAttributes();
                    var colorMap = new ColorMap { OldColor = Color.FromArgb(255, backColor), NewColor = Color.FromArgb(alpha, backColor) };
                    var colorMaps = new ColorMap[] { colorMap };
                    imageAttributes.SetRemapTable(colorMaps);
                }
                alpha = value;
            }
        }

        public Graphics Graphics
        {
            get { return graphics; }
            set { graphics = value; }
        }

        public IntPtr GetHdc()
        {
            if (hdc == IntPtr.Zero)
            {
                graphics = Graphics.FromImage(Bitmap);
                hdc = graphics.GetHdc();
            }
            return hdc;
        }

        public void ReleaseHdc()
        {
            graphics.ReleaseHdc();
            hdc = IntPtr.Zero;
            graphics.Dispose();
        }

        public void Dispose()
        {
            graphics.Dispose();
            hdc = IntPtr.Zero;
        }

        public int x = 0;
    }

    public class PaintHookHelper
    {
        public delegate IntPtr BeginPaintDelegate(IntPtr hWnd, out UnsafeMethods.PAINTSTRUCT paintStruct);

        public delegate bool EndPaintDelegate(IntPtr hWnd, [In] ref UnsafeMethods.PAINTSTRUCT paintStruct);

        private static APIHookWraper b;

        private static APIHookWraper e;

        private static BeginPaintDelegate oldBeginPaint;

        private static EndPaintDelegate oldEndPaint;

        public static Dictionary<IntPtr, PaintingContext> Contexts;


        static PaintHookHelper()
        {
            b = new APIHookWraper();
            e = new APIHookWraper();

            Contexts = new Dictionary<IntPtr, PaintingContext>();

            b.Hook("User32.dll", "BeginPaint", Delegate.CreateDelegate(typeof(BeginPaintDelegate), typeof(PaintHookHelper), "HookBeginPaint"));
            e.Hook("User32.dll", "EndPaint", Delegate.CreateDelegate(typeof(EndPaintDelegate), typeof(PaintHookHelper), "EndPaint"));

            oldBeginPaint = (BeginPaintDelegate)b.RawAPI;
            oldEndPaint = (EndPaintDelegate)e.RawAPI;
        }


        public static IntPtr HookBeginPaint(IntPtr hWnd, out UnsafeMethods.PAINTSTRUCT paintStruct)
        {
            paintStruct = new UnsafeMethods.PAINTSTRUCT();

            if (Contexts.ContainsKey(hWnd))
            {
                PaintingContext context = Contexts[hWnd];
                Monitor.Enter(context);
                IntPtr hdc = context.GetHdc();
                paintStruct.hdc = hdc;
                paintStruct.fErase = 1;
                paintStruct.fIncUpdate = 0;
                paintStruct.rcPaint.top = 0;
                paintStruct.rcPaint.left = 0;
                paintStruct.rcPaint.right = context.ClientRectangle.Right;
                paintStruct.rcPaint.bottom = context.ClientRectangle.Bottom;
                return hdc;
            }

            if (oldBeginPaint != null)
            {
                return oldBeginPaint.Invoke(hWnd, out paintStruct);
            }
            return IntPtr.Zero;
        }

        public static bool EndPaint(IntPtr hWnd, ref UnsafeMethods.PAINTSTRUCT paintStruct)
        {
            if (Contexts.ContainsKey(hWnd))
            {
                PaintingContext context = Contexts[hWnd];
                context.ReleaseHdc();
                Monitor.Exit(context);

                if (context.x++ == 0)
                {
                    Graphics bufferGraphics0 = Graphics.FromImage(context.BufferBitmap);

                    UnsafeMethods.DrawThemeParentBackground(hWnd, bufferGraphics0.GetHdc(), ref paintStruct.rcPaint);

                    bufferGraphics0.ReleaseHdc();

                    bufferGraphics0.FillRectangle(new SolidBrush(Color.FromArgb(30,context.BackColor)),context.ClientRectangle);

                    bufferGraphics0.Dispose();
                }

                Bitmap b = (Bitmap)context.BufferBitmap.Clone();

                Graphics bufferGraphics = Graphics.FromImage(b);

                bufferGraphics.DrawImage(context.Bitmap, context.ClientRectangle, 0, 0, context.Bitmap.Width, context.Bitmap.Height, GraphicsUnit.Pixel, context.ImageAttributes);

                bufferGraphics.Dispose();

                Graphics mock = Graphics.FromHwnd(hWnd);

                mock.DrawImage(b,  0, 0);

                mock.Dispose();

                b.Dispose();

                UnsafeMethods.ValidateRect(hWnd, IntPtr.Zero);

                return true;
            }
            if (oldEndPaint != null)
            {
                return oldEndPaint.Invoke(hWnd, ref paintStruct);
            }
            return false;
        }
    }
}
